package org.yagnus.scalasupport.iterates;
/**
 * This class implements an iterator wrapper that allows us to break out of a for comprehension with 
 * specified behavior with regards to the underlying iterator's "next" after the break;
 * 

 * 
 * <code>
 * val itr = (0 until 10).iterator ;//some iterator *
 * var processed = -1; 
 * for{
 * 	   x<-makeBreakable(itr)
 *     if x.breakAfter{ x.value == 4}
 *    }{
 * 	   //do stuff with x;
 *     processed = x;
 * 	   println(x);
 * }
 * assert(processed=4);
 * assert(itr.next=5);
 * </code>
 * 
 * This code will loop through that pre-existing iterator as much as possible but will stop after the condition inside
 * breakAfter has become true. (we can't really do breakWhen because when we know the current value of x, we'd have done
 * an iterator.next on the underlying iterator and can't push it back in.) There are two other ways to express this:
 * 
 *    if x.breakAfter{ x.value >= 4}
 * 
 * The frowning face operator:
 * 
 *    if { x.value >= 4} >-: x
 *  
 *  and the bleeped-for-letter-word-operator:
 *  
 *    if { x.value >= 4} *&^: x
 *  
 * @author hc.busy
 *
 */
final object BreakableIterator {

    trait BreakableIteratedValue[A] {
        def get : A;
        final def value = get;
        final def apply = get;
        def break : Unit;
        def broken : Boolean;

        /********************************************
         *          * Break after the condition has reached. This leaves the underlying iterator consistent
         ********************************************/
        final def breakAfter(f : ⇒ Boolean) : Boolean = {
            if (broken || f) break;
            true; //never break current
        }

        final def breakAfter(f : A ⇒ Boolean) : Boolean = {
            if (broken || f(get)) break;
            true; //never break current
        }

        final def stopAfter(f : ⇒ Boolean) : Boolean = breakAfter(f);

        /**
         * Frown break
         **/
        final def >-:(f : ⇒ Boolean) : Boolean = breakAfter(f);

        /**
         * Frown break
         **/
        final def >-:(f : A ⇒ Boolean) : Boolean = breakAfter(f);

        /**
         * bleeped 4-lettered curse break
         **/
        final def *&^:(f : ⇒ Boolean) : Boolean = breakAfter(f);

        /**
         * bleeped 4-lettered curse break
         **/
        final def *&^:(f : A ⇒ Boolean) : Boolean = breakAfter(f);

        /********************************************
         * Break when the condition has reached. This leaves the underlying iterator inconsistent
         *********************************************/
        final def breakWhen(f : ⇒ Boolean) : Boolean = {
            if (broken || f) { break; return false; }
            else return true;
        }

        final def breakWhen(f : A ⇒ Boolean) : Boolean = {
            if (broken || f(get)) { break; return false; }
            else return true;
        }

        final def stopWhen(f : ⇒ Boolean) : Boolean = breakWhen(f);

        /**
         * Frown break when
         **/
        final def |-:(f : ⇒ Boolean) : Boolean = breakWhen(f);

        /**
         * Frown break
         **/
        final def |-:(f : A ⇒ Boolean) : Boolean = breakWhen(f);

        override def toString : String = get.toString;

    }

    final class BreakableIterator[A](itr : Iterator[A]) extends Iterator[BreakableIteratedValue[A]] {
        private var breakOut = false;
        class MyBreakableIteratedValue(a : A) extends BreakableIteratedValue[A] {
            override def get = a;
            override def break { breakOut = true };
            override def broken = breakOut;
        }
        final def broken = breakOut;

        final def hasNext : Boolean = !broken && itr.hasNext;
        final def next : BreakableIteratedValue[A] = if (broken) Iterator.empty.next else new MyBreakableIteratedValue(itr.next);

        final def breakable = this;
    }

    implicit final def makeBreakable[A](in : Iterator[A]) = new BreakableIterator(in);
    implicit final def makeBreakable[A](in : Iterable[A]) = new BreakableIterator(in.iterator);

    implicit final def recoverOriginal[A](l : BreakableIteratedValue[A]) : A = l.get;
}